#!/bin/bash

me="${0##*/}"

### DEPENDENCIES ###
# coreutils awk sed xset xssstate xplanet 

### RECOMMENDS ###
# cputool feh wget xdpyinfo

# The script fetches a screensaver timeout via xset and might behave unpredictably if that fails.

###############################################################################
# USER ADJUSTABLE VARIABLES

xplanetdir="$HOME/.xplanet"
imgdir="$xplanetdir/images"
allfeh=1 # set to 1: Use feh for setting the background, not xplanet
tmpfile=/tmp/welrjkht.png

# EARTH MAP
# needs to point to the directory structure created by my get_bluemarble script
bluemarble="$xplanetdir/bluemarble"
prefix="world.topo.bathy" # "world" "world.topo" - the available map variations
suffix="8192x4096_less.png" # resolution and filetype - you probably don't need to change this.
# in the end the image for this month of the year will be linked to $imgdir/earth.jpg, which is what xplanet defaults to

# the scripts directory will be created by this script!
scenes="$xplanetdir/scripts/scenes"
# how ill the scene be chosen from the scene file?
#~ choose_scene="random" # randomly
#~ choose_scene="daily" # daily change the scene, going through $scenes
#~ choose_scene=1 # where n is a positive integer for the nth scene in your scenes file
choose_scene="$1"

delay=60 # sleep between runs/checks
limit=10 # don't let cpu usage go beyond this many percent - only works when cputool is installed

# setting this to 1 makes the script check for a current cloud image and
# download it to the path $cloudimg
# the get_clouds subroutine relies on this repository: github.com/apollo-ng/cloudmap
apollo_ng_clouds=0
  # the following options are relevant only when apollo_ng_clouds == 1
  # this should be present in your xplanet config:
  cloudimg="$imgdir/clouds_5400x2700.jpg"
  maxdiff="10800" # if our cloud image is older than that - 3 hours - check if there's a new one

#~ options=( -num_times 1 -date_format %c -color 0x555555 -pango -fontsize 8 \
      #~ -font "xos4 terminus" -labelpos +20+50 -glare 0 \
      #~ -label_string "Looking at: %t - Origin: %o" )
[[ $allfeh == 1 ]] && options=( -num_times 1 -glare 0 ) || options=( -num_times 1 -glare 0 -transparency )
[[ $allfeh == 1 ]] && geometry="$(xdpyinfo | awk '/dimensions/{print $2}')"

##############################################################################
# HERE BE LIONS

# sleep as a builtin
for file in /usr/lib*/bash/sleep; do
    [ -r "$file" ] && enable -f "$file" sleep && break
done
# Portable enough?

get_clouds() {
# CLOUD MAP for all - praise be: 
# apollo.open-resource.org/mission:log:2014:06:17:new-fresh-global-cloudmap-distribution-service-xplanet

# using this function is configurable; set to anything but 1 equals false
[[ "$apollo_ng_clouds" != 1 ]] && return

#~ __msg__ "Checking for a cloud map newer than $cloudimg"
local_age="$(stat -c%Y "$cloudimg")"
now="$(date +%s)"
__msg__ "Local cloudmap age $local_age; time now $now; difference $((now - local_age))s"
if (( now - local_age > 10800 )); then
  __msg__ "It's been longer than ${maxdiff}s, time to check if there's a newer one..."
  # Get latest remote checksum
  originsha=$(wget https://raw.githubusercontent.com/apollo-ng/cloudmap/master/global.sha256 --no-cache -q -O -)
  originsha="${originsha%% *}"
  # Generate local checksum
  if [ -e "$cloudimg" ]; then
      localsha="$(sha256sum "$cloudimg")"
      localsha="${localsha%% *}"
  fi
  # Check if we're behind origin
  __msg__ "Checksum comparison says remote file is "
  if [ "${originsha}" != "${localsha}" ]; then
      __msg__ "not the same file as local file; getting it."
      newcloudimg="$imgdir/newcloudimg.jpg"
      # Download raw global.jpg from master
      wget https://raw.githubusercontent.com/apollo-ng/cloudmap/master/global.jpg?${originsha} --no-cache -q -O "$newcloudimg"
      # Generate checksum of downloaded file
      newsha="$(sha256sum "$newcloudimg")"
      newsha="${newsha%% *}"
      # Check if download's chksum corresponds to origin
      [ "$newsha" == "$originsha" ] && mv "$newcloudimg" "$cloudimg" || rm "$newcloudimg"
  else
      __msg__ "the same file as local file. Nothing to do."
  fi
else
  __msg__ "That's not longer than ${maxdiff}s, nothing to do."
fi
__msg__ "Back to main loop."
}

set_bg() {
# if -background is found in options (read from current scene) set it as root window background before xplanet even starts
for ((i=1;i<=$#;i++)); do
  [[ "${!i}" == "-background" ]] && ((i++)) && background="${!i}" && break
done
__msg__ "Found background $background"
if [ -r "$background" ]; then
  __msg__ feh --no-fehbg --bg-fill "$background"
  feh --no-fehbg --bg-fill "$background"
elif [ -r "$imgdir/$background" ];then
  __msg__ feh --no-fehbg --bg-fill "$imgdir/$background"
  feh --no-fehbg --bg-fill "$imgdir/$background"
fi
}

__msg__() {
  echo -e "${me}@${SECONDS}s: $@"
}
aftermath() {
  # things to do after root window update
  signal=SIGUSR2
  for pid in $(pidof root-tail); do
    echo "kill -s $signal $pid"
    kill -s $signal $pid
  done
}
allfehornot() {
  if [[ $allfeh == 1 ]]; then
    "$@" -output "$tmpfile" -geometry "$geometry"
    feh --no-fehbg --bg-tile "$tmpfile"
    rm "$tmpfile"
  else
    "$@"
  fi
}
##############################################################################
# MAIN

sstime="$(xset q | awk '/timeout/ {print $2}')" # the screensaver delay time in s

cputool="$(which cputool)"
[ ! -x "$cputool" ] && cputool=0 && limit=100

# EARTH MAP
month="$(date +%m)"
map_real="$bluemarble/$month/$prefix/$prefix.2004$month.3x$suffix"
map_link="$imgdir/earth.jpg"
if [ ! -a "$map_link" ] || [ -h "$map_link" ]]; then
  ln -sf "$map_real" "$map_link"
else
  __msg__ "Not symlinking chosen earth map\n$map_real\nto $map_link\nbecause $map_link\nalready exists and is not a symbolic link."
fi

##############################################################################
# we need to sanitize the scenes file first (mostly because wc -l might return something wrong):
tail -c1 "$scenes" | read -r _ || echo >> "$scenes" # if a trailing newline is missing, add one
sed -i '/^$/d' "$scenes" # if there's empty lines, delete them
allscenes="$(wc -l "$scenes")"
allscenes="$(( ${allscenes%% *} / 2))"
case "$choose_scene" in
  random) chosen=$((RANDOM%allscenes + 1));;
  daily) chosen=$(($(date +%-j)%allscenes + 1));;# %j: day of the year - so we change the scene once a day
  *)  if [ "$choose_scene" -gt 0 ] && [ "$choose_scene" -le "$allscenes" ]; then
        chosen="$choose_scene"
      else
        __msg__ "Wrong value for choose_scene: $choose_scene. Exiting."
        exit 1
      fi;;
esac
__msg__ "Chosen scene #$chosen:"
chosen=$((chosen*2))
sed -n $((chosen - 1))p "$scenes"
eval set -- "$(sed -n ${chosen}p "$scenes")"
__msg__ "All options:\n${options[@]} $@\n"

# if a background image is found in options, set it with feh
set_bg "$@"
aftermath

killall xplanet >/dev/null 2>&1
get_clouds
__msg__ "First xplanet run without CPU throttling ..."
runtime=$SECONDS
###########################
xplanet "${options[@]}" "$@"
###########################
aftermath
runtime=$(( SECONDS - runtime)) # a very rough calculation of how long xplanet took without cpu throttling; this is used to determine
# max. idle time before screensaver kicks in
__msg__ "... That took $runtime seconds"
(( runtime == 0 )) && runtime=1
runtime=$(( runtime * (100/limit) ))
__msg__ "Screensaver delay according to xset: ${sstime}s\nCalculated xplanet runtime with CPU usage limited to ${limit}%: ${runtime}s"
# assume xplanet takes Xs to render things, so if the screensaver comes on in Xs, don't even start rendering
(( sstime > runtime )) && sstime="$((sstime - runtime))" || sstime="$runtime"
__msg__ "If idle for more than ${sstime}s do not start xplanet.\n"
sstime="$((sstime * 1000))" # need it in ms
while sleep "$delay"; do
  # X11 has been idle for too long?
  idle="$(xssstate -i)"
  __msg__ "Current idle time: $((idle / 1000))s"
  if (( idle > sstime )); then
    __msg__ "Not running xplanet because screensaver is going to activate soon."
  else
    if [[ "$cputool" != 0 ]] && ((limit<100)); then
      __msg__ "Starting throttled xplanet"
      allfehornot cputool -c $limit -- xplanet "${options[@]}" "$@"
    else
      __msg__ "Starting xplanet"
      allfehornot xplanet "${options[@]}" "$@"
    fi
      __msg__ "xplanet finished"
  aftermath
  fi
  get_clouds
done  
